home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Plus 2004 #9
/
Amiga Plus CD - 2004 - No. 09.iso
/
amigaplus
/
tools
/
amigaos4_only
/
fracblank
/
source
/
fracblanker.c
< prev
next >
Wrap
C/C++ Source or Header
|
2004-08-03
|
14KB
|
552 lines
/*
** FracBlank - AmigaDOS 2.04 commodities utility screenblanker
**
** Copyright © 1991-1995 by Olaf `Olsen' Barthel
** All Rights Reserved
**
** Cosmic flame fractal code derived from xlock source code
**
** Copyright © 1988-1991 by Patrick J. Naughton.
*/
#include <math.h>
#include <clib/alib_protos.h>
#include <dos/dos.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include "Frac.h"
#include "FracBlanker.h"
// Spread a byte across a long word
#define SPREAD(v) ((ULONG)(v) << 24 | (ULONG)(v) << 16 | (ULONG)(v) << 8 | (v))
// BlankerEntry data
ULONG CycleMask;
LONG CycleBit;
// sin -45° = cos -45° (saves precious calculation time)
double deg45;
// The current fractal type
UBYTE FractalType;
// Screen and pattern change timeout
ULONG ScreenCount = 0,
PatternCount = 0,
ScreenTimeout = 60,
PatternTimeout = 60;
// Declarations for cosmic flame blanker code
double Flame[2][3][2];
WORD FlameLevel,
FlameAlternateForm;
WORD FlameWheel,
FlameColour;
ULONG FlamePoints;
ULONG MaxRecursionLevel = 40,
MaxFlamePoints = 200;
// Prototypes
VOID CosmicFlame();
BOOL RecurseMono(double x, double y, WORD Level);
BOOL RecurseColour(double x, double y, WORD Level);
VOID RealPlane();
ULONG Random(ULONG MaxValue);
VOID MonoPlot(WORD Left, WORD Top);
VOID MultiPlot(ULONG Colour, WORD Left, WORD Top);
VOID BlackScreen();
VOID RotatePalette();
/* BlankerEntry():
*
* The screen blanker itself.
*/
VOID BlankerEntry() {
//LONG CycleBit;
IExec->Forbid();
// Shouldn't go wrong right now, this is the second
// action this task takes, about 16 signal bits
// should still be vacant
if ((CycleBit = IExec->AllocSignal(-1)) != -1) {
CycleMask = (1L << CycleBit);
// Tell the control process we're running
IExec->Signal((struct Task *)BlankerControlProcess,SIG_HANDSHAKE);
// Now step back
IExec->SetTaskPri(SysBase->ThisTask,-20);
IExec->Permit();
// Determine the fractal type
if (FractalType == FRACTAL_COSMIC_FLAME) CosmicFlame();
else {
if (FractalType == FRACTAL_REAL_PLANE) RealPlane();
else {
if (Random(42) >= 21) CosmicFlame();
else RealPlane();
}
}
}
else {
// Wave goodbye
BlankTask = NULL;
IExec->Signal((struct Task *)BlankerControlProcess,SIG_HANDSHAKE);
}
}
/* CosmicFlame():
*
* The cosmic flame screen blanker.
*
* xlock.c - X11 client to lock a display and show a screen saver.
*
* Copyright (c) 1988-91 by Patrick J. Naughton.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*/
VOID CosmicFlame() {
WORD i,
j,
k;
BOOL Alternate = FALSE;
FlameLevel = 0;
// Monochrome mode?
if (DisplayDepth == 1) {
// Go into fractal generation loop
for(;;) {
if (!((FlameLevel++) % MaxRecursionLevel)) {
BlackScreen();
Alternate = !Alternate;
}
if (Alternate) FlameAlternateForm = 0;
else FlameAlternateForm = Random(2) + 2;
for(k = 0; k < 2; k++) {
for(i = 0; i < 2; i++) {
for(j = 0; j < 3; j++) Flame[i][j][k] = ((double)Random(1024)) / 512.0 - 1.0;
}
}
FlamePoints = 0;
RecurseMono(0.0,0.0,0);
}
}
else {
FlameColour = Random(MaxColour - 1) + 1;
for(;;) {
if (!((FlameLevel++) % MaxRecursionLevel)) {
BlackScreen();
Alternate = !Alternate;
}
else {
WORD NewColour;
do NewColour = Random(MaxColour - 1) + 1;
while(NewColour == FlameColour);
FlameColour = NewColour;
}
if (Alternate) FlameAlternateForm = 0;
else FlameAlternateForm = Random(2) + 2;
for(k = 0; k < 2; k++) {
for(i = 0; i < 2; i++) {
for(j = 0; j < 3; j++) Flame[i][j][k] = ((double)Random(1024)) / 512.0 - 1.0;
}
}
FlamePoints = 0;
RecurseColour(0.0,0.0,0);
}
}
}
/* RecurseMono(double x,double y,WORD Level):
*
* Cosmic flame calculation routine (monochrome).
*/
BOOL RecurseMono(double x, double y, WORD Level) {
ULONG Signals = IExec->SetSignal(0,SIG_BREAK | SIG_CHANGE | CycleMask);
// Are we to shut down?
if (Signals & SIG_BREAK) {
IExec->Forbid();
IExec->Signal((struct Task *)BlankerControlProcess,SIG_HANDSHAKE);
IExec->FreeSignal(CycleBit);
IExec->RemTask(NULL);
}
// Return to top level?
if (StopDrawing) {
FlameLevel = 0;
StopDrawing = FALSE;
return(FALSE);
}
// Change the pattern?
if (Signals & SIG_CHANGE) {
FlameLevel = 0;
StopDrawing = FALSE;
return(FALSE);
}
// Cycle the colours?
if (Signals & CycleMask) {
IGraphics->LoadRGB32(VPort,(ULONG *)Colours);
Wheel = (Wheel + 1) % MaxColour;
RotatePalette();
}
if (Level >= MaxRecursionLevel) {
if ((FlamePoints++) > MaxFlamePoints * 100) return(FALSE);
else MonoPlot((WORD)((Width / 2) * (x + 1.0)),(WORD)((Height / 2) * VerticalScale * (y + 1.0)));
}
else {
double nx,ny;
WORD i;
for(i = 0; i < 2; i++) {
nx = Flame[0][0][i] * x + Flame[0][1][i] * y + Flame[0][2][i];
ny = Flame[1][0][i] * x + Flame[1][1][i] * y + Flame[1][2][i];
if (i < FlameAlternateForm) {
nx = sin(nx);
ny = sin(ny);
}
if (!StopDrawing) {
if (!RecurseMono(nx,ny,Level + 1)) return(FALSE);
}
else return(FALSE);
}
}
return(TRUE);
}
/* RecurseColour(double x,double y,WORD Level):
*
* Cosmic flame calculation routine (colour).
*/
BOOL RecurseColour(double x, double y, WORD Level) {
ULONG Signals = IExec->SetSignal(0,SIG_BREAK | SIG_CHANGE | CycleMask);
if (Signals & SIG_BREAK) {
IExec->Forbid();
IExec->Signal((struct Task *)BlankerControlProcess,SIG_HANDSHAKE);
IExec->FreeSignal(CycleBit);
IExec->RemTask(NULL);
}
if (StopDrawing) {
FlameLevel = 0;
StopDrawing = FALSE;
return(FALSE);
}
/* Change the pattern? */
if (Signals & SIG_CHANGE) {
FlameLevel = 0;
StopDrawing = FALSE;
return(FALSE);
}
// Cycle the colours?
if (Signals & CycleMask) {
IGraphics->LoadRGB32(VPort,(ULONG *)Colours);
Wheel = (Wheel + 1) % MaxColour;
RotatePalette();
}
if (Level >= MaxRecursionLevel) {
if ((FlamePoints++) > MaxFlamePoints * 100) return(FALSE);
else MultiPlot(FlameColour,(WORD)((Width / 2) * (x + 1.0)),(WORD)((Height / 2) * VerticalScale * (y + 1.0)));
}
else {
double nx,ny;
WORD i;
for(i = 0; i < 2; i++) {
nx = Flame[0][0][i] * x + Flame[0][1][i] * y + Flame[0][2][i];
ny = Flame[1][0][i] * x + Flame[1][1][i] * y + Flame[1][2][i];
if (i < FlameAlternateForm) {
nx = sin(nx);
ny = sin(ny);
// nx = atan(ny / nx) / 3.14159;
// ny = sqrt(pow(nx,2.0) + pow(ny,2.0)) - 1.0;
}
if (!StopDrawing) {
if (!RecurseColour(nx,ny,Level + 1)) return(FALSE);
}
else return(FALSE);
}
}
return(TRUE);
}
/* RealPlane():
*
* Draw real plane fractals.
*/
VOID RealPlane() {
UWORD OffsetX = Width / 2,OffsetY = Height / 2;
ULONG Signals;
double x = 0,y = 0,yy,a,b,c,sx,sy,mag;
// Are we running in monochrome mode?
if (DisplayDepth == 1) {
// Provide starting numbers for the fractal
// parameters
a = (double)(Random(700) + 5) / 100;
b = (double)(Random(190) + 5) / 100;
c = (double)(Random( 90) + 5) / 100;
mag = (double)(1 << (Random(6) + 2)) * deg45;
// Go into fractal generation loop
for(;;) {
Signals = IExec->SetSignal(0,SIG_BREAK | SIG_CHANGE | CycleMask);
// Are we to shut down?
if (Signals & SIG_BREAK) {
IExec->Forbid();
IExec->Signal((struct Task *)BlankerControlProcess,SIG_HANDSHAKE);
IExec->FreeSignal(CycleBit);
IExec->RemTask(NULL);
}
/* The original formula looks like
* this:
* ½
* x = y - SIGN(x) × ABS(b × x - c)
* y = a - x
*
* I have split the calculation into
* several steps to save time and
* variables.
*/
yy = a - x;
if (x < 0) x = y + sqrt(fabs(b * x - c));
else x = y - sqrt(fabs(b * x - c));
y = yy;
/* The resulting image appears to have
* been rotated by 45°, so we'll
* rotate the pixel coordinates by -45°
*
* x = x × cos(alpha) + y × sin(alpha)
* y = -x × sin(alpha) + y × cos(alpha)
*
* We also magnify the image (i.e. the
* distribution of pixels) in the following
* lines.
*/
sx = mag * ( x + y);
sy = mag * VerticalScale * (-x + y);
// If the pixel happens to reside within
// the boundaries of the screen, draw it.
MonoPlot((WORD)(sx) + OffsetX,(WORD)(sy) + OffsetY);
// Change the pattern?
if (Signals & SIG_CHANGE) {
BlackScreen();
StopDrawing = FALSE;
x = y = 0;
a = (double)(Random(700) + 5) / 100;
b = (double)(Random(190) + 5) / 100;
c = (double)(Random( 90) + 5) / 100;
mag = (double)(1 << (Random(6) + 2)) * deg45;
Signals &= ~CycleMask;
}
// Cycle the colours?
if (Signals & CycleMask) {
IGraphics->LoadRGB32(VPort,(ULONG *)Colours);
Wheel = (Wheel + 1) % MaxColour;
RotatePalette();
}
}
}
else {
UWORD Count = 0;
WORD Colour;
a = (double)(Random(700) + 5) / 100;
b = (double)(Random(190) + 5) / 100;
c = (double)(Random( 90) + 5) / 100;
mag = (double)(1 << (Random(6) + 2)) * deg45;
Colour = Random(MaxColour - 1) + 1;
for(;;) {
Signals = IExec->SetSignal(0,SIG_BREAK | SIG_CHANGE | CycleMask);
if (Signals & SIG_BREAK) {
IExec->Forbid();
IExec->Signal((struct Task *)BlankerControlProcess,SIG_HANDSHAKE);
IExec->FreeSignal(CycleBit);
IExec->RemTask(NULL);
}
yy = a - x;
if (x < 0) x = y + sqrt(fabs(b * x - c));
else x = y - sqrt(fabs(b * x - c));
y = yy;
sx = mag * ( x + y);
sy = mag * VerticalScale * (-x + y);
MultiPlot(Colour,(WORD)(sx) + OffsetX,(WORD)(sy) + OffsetY);
/* Oh well, it's not that easy to
* produce decent colour values for
* the pixels to be rendered.
*
* The following statement will change
* the current drawing pen after exactly
* 1200 pixels have been rendered and will
* pick a new colour between 1 and 31.
*/
if (Count++ >= 1200) {
WORD NewColour;
Count = 0;
do NewColour = Random(MaxColour - 1) + 1;
while(NewColour == Colour);
Colour = NewColour;
}
if (Signals & SIG_CHANGE) {
BlackScreen();
StopDrawing = FALSE;
x = y = 0;
a = (double)(Random(700) + 5) / 100;
b = (double)(Random(190) + 5) / 100;
c = (double)(Random( 90) + 5) / 100;
mag = (double)(1 << (Random(6) + 2)) * deg45;
Signals &= ~CycleMask;
}
if (Signals & CycleMask) {
IGraphics->LoadRGB32(VPort,(ULONG *)Colours);
Wheel = (Wheel + 1) % MaxColour;
RotatePalette();
}
}
}
}
/* Random(ULONG MaxValue):
*
* Simple random number generation routine.
*/
ULONG Random(ULONG MaxValue) {
struct DateStamp ds;
IDOS->DateStamp(&ds);
if (MaxValue) {
static ULONG RandomSeed = 3735879991UL;
RandomSeed = RandomSeed * (((ULONG)ds.ds_Minute) * ((ULONG)ds.ds_Tick)) + 3780343439UL;
RandomSeed = FastRand(RandomSeed);
return(RandomSeed % MaxValue);
}
else return(0);
}
/* MonoPlot(WORD Left,WORD Top):
*
* Set a pixel somewhere on the screen.
*/
VOID MonoPlot(WORD Left, WORD Top) {
if (Left >= 0 && Left < Width && Top >= 0 && Top < Height) IGraphics->WritePixel(RPort,Left,Top);
}
/* MultiPlot(ULONG Colour,WORD Left,WORD Top):
*
* Set a coloured pixel somewhere on the screen.
*/
VOID MultiPlot(ULONG Colour, WORD Left, WORD Top) {
if (Left >= 0 && Left < Width && Top >= 0 && Top < Height) {
if (Pen != Colour) IGraphics->SetAPen(RPort,Pen = Colour);
IGraphics->WritePixel(RPort,Left,Top);
}
}
/* BlackScreen():
*
* Clear the screen, this is accomplished by first setting the
* screen colours to black, clearing the screen and finally
* restoring the palette.
*/
VOID BlackScreen() {
WORD Count;
Count = Colours->NumColours;
Colours->NumColours = MaxColour + 1;
memset(&Colours->Entry[0],0,256 * sizeof(ColourEntry));
IGraphics->LoadRGB32(VPort,(ULONG *)Colours);
IGraphics->SetRast(RPort,0);
Colours->NumColours = Count;
RotatePalette();
IGraphics->LoadRGB32(VPort,(ULONG *)Colours);
}
/* RotatePalette():
*
* Fill in the screen palette with new data.
*/
VOID RotatePalette() {
WORD i,
Index = Wheel;
for(i = 1; i <= MaxColour; i++) {
Colours->Entry[i].Red = SPREAD(Red[Index]);
Colours->Entry[i].Green = SPREAD(Green[Index]);
Colours->Entry[i].Blue = SPREAD(Blue[Index]);
Index = (Index + 1) % MaxColour;
}
// Terminate the table, just in case...
Colours->Entry[Colours->NumColours].Red = 0;
}